package test

import (
	"aigen-todo/internal/api"
	"aigen-todo/internal/config"
	"aigen-todo/internal/models"
	"aigen-todo/internal/service"
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"

	"github.com/gofiber/fiber/v2"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

// TestAuthAPI 测试认证相关API
func TestAuthAPI(t *testing.T) {
	// 创建配置
	cfg := &config.Config{
		JWT: config.JWTConfig{
			Secret: "test_secret_key",
		},
	}

	// 测试用例1：注册成功
	t.Run("RegisterSuccess", func(t *testing.T) {
		// 为每个测试用例创建独立的mock实例
		mockUserRepo := new(MockUserRepository)
		userService := service.NewUserService(mockUserRepo)
		
		// 创建Fiber应用
		app := fiber.New()
		
		// 注册路由
		public := app.Group("/api/v1")
		public.Post("/register", api.RegisterHandler(userService))
		
		// 设置模拟返回值 - 用户不存在时返回错误
		mockUserRepo.On("GetByUsername", "testuser").Return(nil, gorm.ErrRecordNotFound)
		mockUserRepo.On("GetByEmail", "test@example.com").Return(nil, gorm.ErrRecordNotFound)
		mockUserRepo.On("Create", mock.AnythingOfType("*models.User")).Return(nil)

		// 创建请求体
		reqBody, _ := json.Marshal(map[string]interface{}{
			"username": "testuser",
			"email":    "test@example.com",
			"password": "password123",
		})

		// 创建HTTP请求
		req := httptest.NewRequest(http.MethodPost, "/api/v1/register", bytes.NewBuffer(reqBody))
		req.Header.Set("Content-Type", "application/json")

		// 执行请求
		resp, err := app.Test(req)
		assert.NoError(t, err)
		assert.Equal(t, http.StatusOK, resp.StatusCode)

		// 解析响应
		var response map[string]interface{}
		json.NewDecoder(resp.Body).Decode(&response)
		assert.True(t, response["success"].(bool))
		assert.Equal(t, "注册成功", response["message"])

		mockUserRepo.AssertExpectations(t)
	})

	// 测试用例2：登录成功
	t.Run("LoginSuccess", func(t *testing.T) {
		// 为每个测试用例创建独立的mock实例
		mockUserRepo := new(MockUserRepository)
		userService := service.NewUserService(mockUserRepo)
		
		// 创建Fiber应用
		app := fiber.New()
		
		// 注册路由
		public := app.Group("/api/v1")
		public.Post("/login", api.LoginHandler(userService, cfg))
		
		// 设置模拟返回值
		hashedPassword, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
		mockUserRepo.On("GetByUsername", "testuser").Return(&models.User{
			ID:       1,
			Username: "testuser",
			Password: string(hashedPassword),
		}, nil)

		// 创建请求体
		reqBody, _ := json.Marshal(map[string]interface{}{
			"username": "testuser",
			"password": "password123",
		})

		// 创建HTTP请求
		req := httptest.NewRequest(http.MethodPost, "/api/v1/login", bytes.NewBuffer(reqBody))
		req.Header.Set("Content-Type", "application/json")

		// 执行请求
		resp, err := app.Test(req)
		assert.NoError(t, err)
		assert.Equal(t, http.StatusOK, resp.StatusCode)

		// 解析响应
		var response map[string]interface{}
		json.NewDecoder(resp.Body).Decode(&response)
		assert.True(t, response["success"].(bool))
		assert.Equal(t, "登录成功", response["message"])
		assert.NotEmpty(t, response["data"].(map[string]interface{})["token"])

		mockUserRepo.AssertExpectations(t)
	})

	// 测试用例3：登录失败 - 用户不存在
	t.Run("LoginUserNotFound", func(t *testing.T) {
		// 为每个测试用例创建独立的mock实例
		mockUserRepo := new(MockUserRepository)
		userService := service.NewUserService(mockUserRepo)
		
		// 创建Fiber应用
		app := fiber.New()
		
		// 注册路由
		public := app.Group("/api/v1")
		public.Post("/login", api.LoginHandler(userService, cfg))
		
		// 设置模拟返回值
		mockUserRepo.On("GetByUsername", "nonexistent").Return(nil, gorm.ErrRecordNotFound)
		mockUserRepo.On("GetByEmail", "nonexistent").Return(nil, gorm.ErrRecordNotFound)

		// 创建请求体
		reqBody, _ := json.Marshal(map[string]interface{}{
			"username": "nonexistent",
			"password": "password123",
		})

		// 创建HTTP请求
		req := httptest.NewRequest(http.MethodPost, "/api/v1/login", bytes.NewBuffer(reqBody))
		req.Header.Set("Content-Type", "application/json")

		// 执行请求
		resp, err := app.Test(req)
		assert.NoError(t, err)
		assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)

		// 解析响应
		var response map[string]interface{}
		json.NewDecoder(resp.Body).Decode(&response)
		assert.False(t, response["success"].(bool))

		mockUserRepo.AssertExpectations(t)
	})
}
